package com.qdlc.p2p.biz.service.impl;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.google.common.base.Splitter;
import com.google.common.primitives.Longs;
import com.qd.p2p.adapter.Response;
import com.qd.p2p.adapter.enums.ResponseStatus;
import com.qd.p2p.sina.pay.enums.IdentityType;
import com.qdlc.p2p.biz.executer.impl.NewActivityAwardExecutor;
import com.qdlc.p2p.biz.processor.event.TenderNoticeEvent;
import com.qdlc.p2p.biz.service.AbstractCollectService;
import com.qdlc.p2p.biz.service.AccountLogService;
import com.qdlc.p2p.biz.service.AccountService;
import com.qdlc.p2p.biz.service.CollectPayService;
import com.qdlc.p2p.biz.service.InterestService;
import com.qdlc.p2p.biz.service.RPCRequestService;
import com.qdlc.p2p.biz.util.NoticeTemplateSender;
import com.qdlc.p2p.common.constant.BorrowStatus;
import com.qdlc.p2p.common.constant.BorrowTenderStatus;
import com.qdlc.p2p.common.constant.BorrowTenderType;
import com.qdlc.p2p.common.constant.BorrowTimeType;
import com.qdlc.p2p.common.constant.GeneralAccountType;
import com.qdlc.p2p.common.constant.Global;
import com.qdlc.p2p.common.constant.LogTplNid;
import com.qdlc.p2p.common.constant.NoticeTplNid;
import com.qdlc.p2p.common.constant.NotifyUrl;
import com.qdlc.p2p.common.constant.RateInterestStatus;
import com.qdlc.p2p.common.constant.RedEnvelopeType;
import com.qdlc.p2p.common.constant.RedUseType;
import com.qdlc.p2p.common.constant.Summary;
import com.qdlc.p2p.common.constant.TradeCode;
import com.qdlc.p2p.common.constant.TradeStatus;
import com.qdlc.p2p.common.core.ReturnResult;
import com.qdlc.p2p.common.core.queue.QueueHandlers;
import com.qdlc.p2p.common.enums.EnumActicityPlanNid;
import com.qdlc.p2p.common.exception.BusinessException;
import com.qdlc.p2p.common.exception.RedEnvelopeException;
import com.qdlc.p2p.common.util.BigDecimalUtil;
import com.qdlc.p2p.common.util.DataUtils;
import com.qdlc.p2p.common.util.DateUtil;
import com.qdlc.p2p.common.util.MailUtil;
import com.qdlc.p2p.dal.common.SystemConfigHelper;
import com.qdlc.p2p.dal.dao.BorrowDao;
import com.qdlc.p2p.dal.dao.BorrowTenderDao;
import com.qdlc.p2p.dal.dao.RateInterestDao;
import com.qdlc.p2p.dal.dao.UseRateLogDao;
import com.qdlc.p2p.dal.dao.UserRedEnvelopeDao;
import com.qdlc.p2p.dal.dao.UserStatisticsDao;
import com.qdlc.p2p.dal.dto.Borrow;
import com.qdlc.p2p.dal.dto.BorrowTender;
import com.qdlc.p2p.dal.dto.RateInterest;
import com.qdlc.p2p.dal.dto.TppTradeLog;
import com.qdlc.p2p.dal.dto.UseRateLog;
import com.qdlc.p2p.dal.dto.User;
import com.qdlc.p2p.dal.model.BorrowModel;
import com.qdlc.p2p.dal.model.interest.InterestCalculator;
import com.qdlc.p2p.dal.util.UserUtils;

/**
 * 投标 代收服务类
 *
 * @author wuaj
 * @version 1.1.0.0
 */
@Service("borrowTenderCollectService")
public class BorrowTenderCollectServiceImpl extends AbstractCollectService {

    private final static String BUSINESS_NAME = "投标代收-";

    /**
     * 标Model
     */
    private ThreadLocal<BorrowModel> borrowModelLocal = new ThreadLocal<BorrowModel>();

    /**
     * 投标用户
     */
    private ThreadLocal<User> tenderUserLocal = new ThreadLocal<User>();

    @Resource
    private UserRedEnvelopeDao userRedEnvelopeDao;

    /**
     * 红包代收业务处理
     */
    @Resource
    @Qualifier("redEnvelopeCollectService")
    private CollectPayService redEnvelopeCollectService;

    @Resource
    private RateInterestDao rateInterestDao;

    @Resource
    private BorrowDao borrowDao;

    @Resource
    private BorrowTenderDao borrowTenderDao;

    @Resource
    private UseRateLogDao useRateLogDao;

    @Resource
    private UserStatisticsDao userStatisticsDao;

    @Resource
    private InterestService interestService;

    @Resource
    private AccountService accountService;

    @Resource
    private AccountLogService accountLogService;

    @Resource
    @Qualifier("bfRPCRequestService")
    private RPCRequestService<Response, TppTradeLog> rpcRequestService;

    @Override
    public void analyzePars(Object... pars) {
        if (pars.length != 2) {
            throw new IllegalArgumentException("参数个数正确!");
        }
        try {
            this.borrowModelLocal.set((BorrowModel) pars[0]); // 标
            this.tenderUserLocal.set((User) pars[1]); // 投标用户
        } catch (Exception ex) {
            throw new IllegalArgumentException("参数类型正确!");
        }
    }

    @Override
    public boolean preCreateOrder() {
        BorrowModel borrowModel = borrowModelLocal.get();
        User tenderUser = tenderUserLocal.get();

        Map<String, Object> memo = DataUtils.getMemo(borrowModel.getParam());

        Object objRedIds = memo.get("redIds");
        if (objRedIds != null) {
            long[] redIds = Longs.toArray(((ArrayList) objRedIds));
            // 检测红包是否可用
            boolean isCashEnvelope = userRedEnvelopeDao.checkCanUseRedEnvelope(redIds, tenderUser.getUserId(), RedEnvelopeType.VIRTUAL);
            if (isCashEnvelope == false) {
                logger.info(getBusinessName().concat("红包无法使用!"));
                throw new BusinessException("红包无法使用!", RedEnvelopeException.TYPE_JSON);
            }
        }

        Object rateId = memo.get("rateId");
        if (rateId != null) {
            long rateInterestId = DataUtils.toLong(rateId); // 加息券ID
            // 检测加息券是否可用
            RateInterest rateInterest = rateInterestDao.getRateInterestById(rateInterestId);
            if (rateInterest.getStatus() != RateInterestStatus.NORMAL) {
                logger.info(MessageFormat.format(getBusinessName().concat("加息券无法使用!状态:{0}"), rateInterest.getStatus()));
                throw new BusinessException("加息券无法使用!", RedEnvelopeException.TYPE_JSON);
            }
            // 更新加息券为 => 正在使用中
            rateInterest.setStatus(RateInterestStatus.USEING);
            rateInterestDao.update(rateInterest);
        }

        // TODO 第三方可用余额 大于 投标金额 不允许投标
        /*QueryBalanceResponse queryBalanceResponse = sinaPayService.queryBalance(tppId);
        if (tenderAccount > queryBalanceResponse.getAvailableBalance()) {
            throw new BorrowException("您的托管资金还存在未处理记录,无足够金额投标,请耐心等待!", "/invest/" + uuid + "/detail.html");
        }*/
        return true;
    }

    @Override
    public TppTradeLog getTppTradeLog() {
        BorrowModel borrowModel = borrowModelLocal.get();
        Map<String, Object> memo = DataUtils.getMemo(borrowModel.getParam());
        String tppId = (String) memo.get("tppUserCustId"); // 投标用户标识

        TppTradeLog tppTradeLog = new TppTradeLog();
        tppTradeLog.setTppUserCustId(tppId); // 投标用户
        tppTradeLog.setToTppUserCustId(UserUtils.getUserTppCustId(borrowModel.getUser().getUserId())); // 发标用户
        tppTradeLog.setTradeType(TradeCode.C1001.toString());
        tppTradeLog.setBorrowId(borrowModel.getId());
        tppTradeLog.setMoney(borrowModel.getMoney());
        tppTradeLog.setIdentityType(IdentityType.UID.toString());
        tppTradeLog.setMemo(borrowModel.getParam());
        tppTradeLog.setBorrowRepaymentId(0);
        tppTradeLog.setServiceType(Summary.TENDER.getSummaryType());
        return tppTradeLog;
    }

    @Override
    public void aftCreateOrder(TppTradeLog tppTradeLog) {
        BorrowModel borrowModel = borrowModelLocal.get();
        borrowModel.setTenderBilNo(tppTradeLog.getOrderNo());
        borrowModel.setTenderBilDate(DateUtil.dateStr3(tppTradeLog.getAddTime()));

        User tenderUser = tenderUserLocal.get();
        // 生成投标记录 利息在复审时计算
        Borrow borrow = borrowDao.find(borrowModel.getId()); // 标
        BorrowTender borrowTender;
        if (borrowModel.getTenderType() == BorrowTenderType.FROM_AUTO) {
            borrowTender = saveBorrowTender(borrowModel, borrow, tenderUser, BorrowTenderType.FROM_AUTO);
        } else {
            borrowTender = saveBorrowTender(borrowModel, borrow, tenderUser, BorrowTenderType.FROM_SITE);
        }
        Global.setTransfer("tenderId", borrowTender.getId());// 保存投标ID

        double tenderTotalAccount = borrow.getAccount();  // 标总金额
        double accountYes = BigDecimalUtil.add(borrow.getAccountYes(), borrowModel.getMoney()); // 已投金额
        double scales = BigDecimalUtil.div(accountYes, tenderTotalAccount) * 100; // 投资比例
        // 重新计算 避免round而导致99.99变成100
        scales = scales >= 99.99 && scales < 100 ? 99.99 : BigDecimalUtil.round(scales); // 投资比例

        // 更新投标金额 投标次数作为更新条件 保存投标的原子性
        borrowDao.tenderUpdate(borrow.getId(), borrowModel.getMoney(), scales, borrow.getStatus(), borrow.getTenderTimes());

        // 冻结投标金额
        Global.setTransfer(LogTplNid.LOG_TPL_KEY, LogTplNid.TENDER_FREEZE);
        Global.setTransfer("web_url", WEB_URL);
        Global.setTransfer("borrow", borrow);
        Global.setTransfer("money", borrowModel.getMoney());
        accountService.freeze(tenderUser.getUserId(), tenderUser.getUserId(), borrowModel.getMoney());
    }

    @Override
    public ReturnResult requestOrder(TppTradeLog tppTradeLog) {
        Response response = rpcRequestService.tenderRequest(tppTradeLog);
        // 未知状态通知 交由消息队列处理
        if (response == null || response.getStatus() == ResponseStatus.UN_KNOW) {
            notice(tppTradeLog, TradeStatus.TRADE_STATUS_UN_KNOW);
            return new ReturnResult(true, "处理中!请查看投标结果!");
        }

        if (response.getStatus() == ResponseStatus.SUCCESS) {
            tppTradeLog.setRemark(getBusinessName().concat("提交成功"));
            tppTradeService.update(tppTradeLog);

            // 成功通知
            notice(tppTradeLog, TradeStatus.TRADE_STATUS_FINISHED);
            requestOrderSuccess(tppTradeLog);
            // 失败
        } else if (response.getStatus() == ResponseStatus.FAILURE) {
            String message = response == null ? "RPC调用失败" : MessageFormat.format("{0}失败!ResponseCode:{1} ResponseMsg:{2}",
                    getBusinessName(), response.getResponseCode(),
                    response.getResponseMsg());
            String responseMessage = response == null ? "RPC调用失败" : response.getResponseMsg();
            logger.info(message);

            // 置为废单
            tppTradeLog.setTradeStatus(TradeStatus.TRADE_STATUS_INVALID.getStatus());
            tppTradeLog.setRemark(getBusinessName().concat("提交失败-" + responseMessage));
            tppTradeService.update(tppTradeLog);

            requestOrderFail(tppTradeLog); // 请求失败处理
            return new ReturnResult(true, "投标失败 - 系统繁忙请重试!");
        }
        return ReturnResult.SUCCESS;
    }

    @Override
    public void requestOrderSuccess(TppTradeLog tppTradeLog) {
        // 红包兑现
        Map<String, Object> memoMap = DataUtils.getMemo(tppTradeLog.getMemo());
        Object redIdsObj = memoMap.get("redIds");
        if (redIdsObj != null) {
            long[] redEnvelopeIds = Longs.toArray(((ArrayList) redIdsObj)); // 红包ID
            virtualRedEnvelopeExchange(redEnvelopeIds, tenderUserLocal.get().getUserId());
        }
    }

    @Override
    public void requestOrderFail(TppTradeLog tppTradeLog) {
        double tenderAccount = tppTradeLog.getMoney(); // 投标金额
        boolean isCallBackFail = tenderUserLocal.get() == null;

        String tenderBilNo = tppTradeLog.getOrderNo();// 代收订单号
        BorrowTender borrowTender = borrowTenderDao.getTenderByBillNo(tenderBilNo);
        if (borrowTender != null && borrowTender.getStatus() == BorrowTenderStatus.TREAT) {
            Borrow borrow = borrowTender.getBorrow(); // 投的标
            if (borrow.getStatus() != BorrowStatus.STATUS_TRIAL_PASSED) {
                throw new BusinessException(MessageFormat.format("投标代收-非初审标,无法处理!状态:{0}", borrow.getStatus()));
            }

            User tenderUser = isCallBackFail == true ? borrowTender.getUser() : tenderUserLocal.get(); // 投标用户
            double tenderTotalAccount = borrow.getAccount();  // 标总金额
            // 更新已投金额 (已投金额 - 投标金额)
            double accountYes = BigDecimalUtil.add(borrow.getAccountYes(), -tenderAccount); // 已投金额
            // 投资比例退回
            double scales = BigDecimalUtil.div(accountYes, tenderTotalAccount) * 100;
            // 避免round而导致99.99变成100
            scales = scales >= 99.99 && scales < 100 ? 99.99 : BigDecimalUtil.round(scales);
            // 更新投标金额 投标次数作为更新条件 保存投标的原子性
            borrowDao.tenderUpdate(tppTradeLog.getBorrowId(), -tenderAccount, scales, borrow.getStatus(), borrow.getTenderTimes());
            logger.info(MessageFormat.format("用户:{0} 订单号:{1} 返还投标金额:{2} ", tenderUser.getRealName(), tppTradeLog.getOrderNo(), tenderAccount));

            // 解冻 投标金额
            Global.setTransfer(LogTplNid.LOG_TPL_KEY, LogTplNid.TENDER_UN_FREEZE);
            Global.setTransfer("web_url", WEB_URL);
            Global.setTransfer("borrow", borrow);
            Global.setTransfer("money", tenderAccount);
            long userId = tenderUser.getUserId();
            accountService.unFreeze(userId, userId, tenderAccount);
            logger.info(MessageFormat.format("用户:{0} 订单号:{1} 解冻投标金额:{2} ", tenderUser.getRealName(), tppTradeLog.getOrderNo(), tenderAccount));

            // 加息券变为可用
            Map<String, Object> memo = DataUtils.getMemo(tppTradeLog.getMemo());
            Object rateId = memo.get("rateId");
            if (rateId != null) {
                long rateInterestId = DataUtils.toLong(rateId); // 加息券ID
                // 检测加息券是否正在使用中
                RateInterest rateInterest = rateInterestDao.getRateInterestById(rateInterestId);
                // 更新加息券为 => 可用
                rateInterest.setStatus(RateInterestStatus.NORMAL);
                rateInterestDao.update(rateInterest);
            }
            // 投标状态更新为无效
            borrowTender.setStatus(isCallBackFail == true ? BorrowTenderStatus.FAIL : BorrowTenderStatus.INVALID);
            borrowTenderDao.update(borrowTender);
        }
    }

    @Override
    public void tradeFinish(TppTradeLog tppTradeLog, String depositStatus, double tradeAmount) {
        super.tradeFinishForStatus(tppTradeLog, depositStatus, tradeAmount);

        String orderNo = tppTradeLog.getOrderNo();// 代收订单号

        Map<String, Object> memo = DataUtils.getMemo(tppTradeLog.getMemo());
        double redEnvelopeMoney = DataUtils.toDouble(memo.get("redMoney")); // 红包金额

        // 投标是否待处理状态
        BorrowTender borrowTender = borrowTenderDao.getTenderByBillNo(orderNo);
        if (borrowTender == null) {
            throw new BusinessException(MessageFormat.format("投标代收回调 - 投标记录不存在! 订单号:{0}", orderNo));
        }

        if (borrowTender.getStatus() == BorrowTenderStatus.PROCESSED) {
            logger.info("该投标状态已被处理!");
            return;
        }

        if (borrowTender.getStatus() != BorrowTenderStatus.TREAT) {
            throw new BusinessException(MessageFormat.format("投标代收回调 - 该投标状态不正确,确认是否被修改过! 标状态:{0} 订单号:{1}", borrowTender.getStatus(), orderNo));
        }

        Borrow borrow = borrowTender.getBorrow(); // 投的标
        User tenderUser = borrowTender.getUser(); // 投标用户

        // 更新投标数,新手标使用,不管是否投标成功
        userStatisticsDao.updateStatisticsCount(tenderUser.getUserId(), true);

        double amount = borrowTender.getAccount(); // 投标金额

        // 投标更新为已处理及计算利息
        borrow.setIsTender(BorrowTenderStatus.SUCCESS);// 用于标记是投标的情况
        InterestCalculator interestCalculator = interestService.interestCalculator(borrow, amount);
        borrowTender = updateTenderToSuccess(borrowTender, interestCalculator);

        // 投标成功 只增加投标成功日志(资金在放款时扣除)
        Global.setTransfer(LogTplNid.LOG_TPL_KEY, LogTplNid.TENDER_SUCCESS);
        Global.setTransfer("web_url", WEB_URL);
        Global.setTransfer("borrow", borrow);
        Global.setTransfer("tenderAmount", tradeAmount);
        Global.setTransfer("money", tppTradeLog.getMoney());
        Global.setTransfer("redEnvelopeMoney", redEnvelopeMoney);
        borrowTenderDao.update(borrowTender);
        accountLogService.addAccountLog(tenderUser.getUserId(), GeneralAccountType.IDC, tradeAmount);

        // 处理加息券
        long rateInterestId = DataUtils.toLong(memo.get("rateId")); // 加息券
        if (rateInterestId > 0) {
            RateInterest rateInterest = rateInterestDao.getRateInterestById(rateInterestId);
            // 添加加息券使用记录
            UseRateLog rateLog = new UseRateLog();
            rateLog.setBorrowId(borrow.getId());
            rateLog.setRateId(rateInterestId);
            rateLog.setTenderId(borrowTender.getId());
            rateLog.setUseTime(DateUtil.getNow());
            rateLog.setUpApr(rateInterest.getUpApr());
            rateLog.setNewApr(BigDecimalUtil.add(borrow.getApr(), rateInterest.getUpApr()));
            rateLog.setUserId(tenderUser.getUserId());
            useRateLogDao.save(rateLog);

            // 更新加息券状态 => 使用
            rateInterest.setStatus(RateInterestStatus.USE);
            rateInterest.setUseTime(DateUtil.getNow());
            rateInterestDao.update(rateInterest);
        }

        // 投标成功活动环节
        // 按月标活动环节
        List<String> byMonthTenderActives = Splitter.on(",").splitToList(SystemConfigHelper.getValue(SystemConfigHelper.Nid.BY_MONTH_TENDER_ACTIVE));
        if(BorrowTimeType.MONTH == borrow.getBorrowTimeType() && byMonthTenderActives.contains(String.valueOf(borrow.getTimeLimit()))) {
            NewActivityAwardExecutor.giveByTache(tenderUser, EnumActicityPlanNid.BY_MONTH_TENDER_SUCCESS.getValue());
        }
        NewActivityAwardExecutor.giveByTache(tenderUser, EnumActicityPlanNid.TENDER.getValue());

        // 给投资人发送通知
        Map<String, Object> sendData = new HashMap<String, Object>();
        sendData.put("tender", borrowTender);
        sendData.put("user", tenderUser);
        sendData.put("borrow", borrow);
        NoticeTemplateSender sender = NoticeTemplateSender.newInstance(tenderUser, NoticeTplNid.TENDER);
        sender.send(sendData);
    }

    @Override
    public void tradeSuccess(TppTradeLog tppTradeLog, String depositStatus) {
        super.tradeSuccessForStatus(tppTradeLog, depositStatus);
    }

    @Override
    public void tradeFail(TppTradeLog tppTradeLog, String depositStatus) {
        super.tradeFailForStatus(tppTradeLog, depositStatus);
        // 回调失败
        requestOrderFail(tppTradeLog);
    }

    @Override
    public String getCallBackUrl() {
        return WEB_URL.concat(NotifyUrl.TENDER_NOTIFY_COLLECT_URL).concat(".html");
    }

    @Override
    public String getBusinessName() {
        return BUSINESS_NAME;
    }

    /**
     * 保存投标记录
     *
     * @param model      标模型
     * @param borrow     标
     * @param tenderType 来源
     */
    private BorrowTender saveBorrowTender(BorrowModel model, Borrow borrow, User tenderUser, byte tenderType) {
        BorrowTender borrowTender = new BorrowTender();
        borrowTender.setBorrow(borrow);
        borrowTender.setMoney(model.getMoney()); // 投标金额
        borrowTender.setTenderBilNo(model.getTenderBilNo()); // 订单号
        borrowTender.setTenderBilDate(DateUtil.dateStr3(DateUtil.getNow())); // 投标日期
        borrowTender.setAddTime(DateUtil.getNow());  // 投标时间
        borrowTender.setAddIp(model.getAddIp()); // IP地址
        borrowTender.setUser(tenderUser); // 投标用户
        borrowTender.setTenderType(tenderType); // 投标来源
        borrowTender.setAccount(model.getMoney());// 体验券金额或投标金额
        borrowTender.setStatus(BorrowTenderStatus.TREAT); // 待处理
        borrowTender.setRealAmount(model.getMoney()); // 投标金额
        borrowTenderDao.save(borrowTender);
        return borrowTender;
    }

    /**
     * 虚拟红包兑现
     */
    private void virtualRedEnvelopeExchange(long[] redEnvelopeIds, long exchangeUserId) {
        try {
            TppTradeLog tppTradeLog = redEnvelopeCollectService.createOrder(redEnvelopeIds, exchangeUserId, RedEnvelopeType.VIRTUAL, RedUseType.BORROW_TENDER);
            ReturnResult returnResult = redEnvelopeCollectService.requestOrder(tppTradeLog);
            if (returnResult.isError()) {
                // do nothing
                MailUtil.sendMail("投标 - 兑现红包 - 代收提交成功 - 兑换失败", "虚拟红包兑现失败", returnResult.getErrorInfo());
            }
        } catch (Exception ex) {
            MailUtil.sendMail("投标 - 兑现红包 - 代收提交成功 - 兑换失败", "虚拟红包兑现失败", "");
        }
    }

    /**
     * 修改投标状态为已处理
     */
    private BorrowTender updateTenderToSuccess(BorrowTender tender, InterestCalculator ic) {
        // 总共需要还款金额
        double repayment_account = ic.repayTotal();
        // tender标利息总和
        double totalInterest = BigDecimalUtil.sub(repayment_account, tender.getAccount());
        tender.setRepaymentAccount(repayment_account);
        tender.setInterest(totalInterest);
        tender.setStatus(BorrowTenderStatus.PROCESSED);
        borrowTenderDao.modifyBorrowTender(tender);
        return tender;
    }

    /**
     * 发送投标通知 交由队列来执行
     */
    private void notice(TppTradeLog tppTradeLog, TradeStatus tradeStatus) {
        TenderNoticeEvent event = new TenderNoticeEvent();
        event.setClientIp(Global.getIP());
        event.setTradeStatus(tradeStatus);
        event.setTppTradeLog(tppTradeLog);
        event.setOutOrderNo(tppTradeLog.getOrderNo());
        event.setDepositAmount(tppTradeLog.getMoney());
        QueueHandlers.put(event);
    }
}